home *** CD-ROM | disk | FTP | other *** search
- ::/ \::::::.
- :/___\:::::::.
- /| \::::::::.
- :| _/\:::::::::.
- :| _|\ \::::::::::. July-Sep 99
- :::\_____\::::::::::. Issue 5
- ::::::::::::::::::::::.........................................................
-
- A S S E M B L Y P R O G R A M M I N G J O U R N A L
- http://asmjournal.freeservers.com
- asmjournal@mailcity.com
-
-
-
-
- T A B L E O F C O N T E N T S
- ----------------------------------------------------------------------
- Introduction...................................................mammon_
-
- "COM in Assembly Part II"...................................Bill.Tyler
-
- "How to use DirectDraw in ASM"...............................X-Calibre
-
- "Writing Boot Sectors To Disk"...........................Jan.Verhoeven
-
- "Dumping Memory to Disk".................................Jan.Verhoeven
-
- "Formatted Numeric Output"..............................Laura.Fairhead
-
- "Linked Lists in ASM"..........................................mammon_
-
- Column: Win32 Assembly Programming
- "Structured Exception Handling under Win32"...........Chris.Dragan
- "Child Window Controls"...................................Iczelion
- "Dialog Box as Main Window"...............................Iczelion
- "Standardizing Win32 Callback Procedures"............Jeremy.Gordon
-
- Column: The Unix World
- "Fire Demo ported to Linux SVGAlib".................Jan.Wagemakers
-
- Column: Assembly Language Snippets
- "Abs".................................................Chris.Dragan
- "Min".................................................Chris.Dragan
- "Max".................................................Chris.Dragan
- "OBJECT"...................................................mammon_
-
- Column: Issue Solution
- "Binary to ASCII"....................................Jan.Verhoeven
-
- ----------------------------------------------------------------------
- ++++++++++++++++++Issue Challenge+++++++++++++++++
- Convert a bit value to ACIII less than 10 bytes
- ----------------------------------------------------------------------
-
-
-
- ::/ \::::::.
- :/___\:::::::.
- /| \::::::::.
- :| _/\:::::::::.
- :| _|\ \::::::::::.
- :::\_____\:::::::::::..............................................INTRODUCTION
- by mammon_
-
-
- I suppose I should start with the good news. A week or so ago Hiroshimator
- emailed me for the nth time asking if I needed help with the journal as I have
- yet to get one out on time. I relented and asked if he knew any listservers;
- one hour later he had an account for APJ set up at e-groups, specifically:
- http://www.egroups.com/group/apj-announce
- One of the greatest obstacles to putting out these issues -- processing the
- 300 or so subscription requests that rack up between issues -- is now out of
- the way for good.
-
- The articles this month have somewhat of a high-level focus; with the COM and
- Direct Draw by Bill Tyler and X-Caliber, respectively, as well as Chris
- Dragan's classic work on exception handling and Jeremy Gordon's treatment of
- windows callbacks, this issue is heavily weighed towards high-level win32
- coding. Add to this Iczelion's two tutorials and my own win32-biased
- linked list example, and it appears the DOS/Unix camp is losing ground.
-
- To shore up the Unix front line, Jan Wagemakers has provided a port of last
- month's fire demo to linux [GAS]. In addition, there are A86 articles by Jan
- Verhoeven and a general assembly routine by Laura Fairhead to prove that not
- all assembly has to be 32-bit.
-
- And, finally, I am looking for a good 'challenge' columnist: someone to write
- the monthly APJ challenges [and their solutions] so that I can start
- announcing next month's challenge sooner than next month...
-
- Now at last I can sleep ;)
-
- _m
-
-
- ::/ \::::::.
- :/___\:::::::.
- /| \::::::::.
- :| _/\:::::::::.
- :| _|\ \::::::::::.
- :::\_____\:::::::::::...........................................FEATURE.ARTICLE
- COM in Assembly Part II
- by Bill Tyler
-
-
- My previous atricle described how to use COM objects in your assembly
- language programs. It described only how to call COM methods, but not how to
- create your own COM objects. This article will describe how to do that.
-
- This article will describe implementing COM Objects, using MASM syntax. TASM
- or NASM assemblers will not be considered, however the methods can be easily
- applied to any assembler.
-
- This article will also not describe some of the more advanced features of COM
- such as reuse, threading, servers/clients, and so on. These will presented
- in future articles.
-
-
- COM Interfaces Review
- ------------------------------------------------------------------------------
- An interface definition specifies the interface's methods, their return types,
- the number and types of their parameters, and what the methods must do. Here
- is a sample interface definition:
-
- IInterface struct
- lpVtbl dd ?
- IInterface ends
-
- IInterfaceVtbl struct
- ; IUnknown methods
- STDMETHOD QueryInterface, :DWORD, :DWORD, :DWORD
- STDMETHOD AddRef, :DWORD
- STDMETHOD Release, :DWORD
- ; IInterface methods
- STDMETHOD Method1, :DWORD
- STDMETHOD Method2, :DWORD
- IInterfaceVtbl ends
-
- STDMETHOD is used to simplify the interface declaration, and is defined as:
-
- STDMETHOD MACRO name, argl :VARARG
- LOCAL @tmp_a
- LOCAL @tmp_b
- @tmp_a TYPEDEF PROTO argl
- @tmp_b TYPEDEF PTR @tmp_a
- name @tmp_b ?
- ENDM
-
- This macro is used to greatly simplify interface declarations, and so that the
- MASM invoke syntax can be used. (Macro originally by Ewald :)
-
- Access to the interface's methods occurs through a pointer. This pointer
- points to a table of function pointers, called a vtable. Here is a sample
- method call:
-
- mov eax, [lpif] ; lpif is the interface pointer
- mov eax, [eax] ; get the address of the vtable
- invoke (IInterfaceVtbl [eax]).Method1, [lpif] ; indirect call to the function
- - or -
- invoke [eax][IInterfaceVtbl.Method2], [lpif] ; alternate notation
-
- Two different styles of addressing the members are shown. Both notations
- produce equivalent code, so the method used is a matter of personal
- preference.
-
- All interfaces must inherit from the IUnknown interface. This means that the
- first 3 methods of the vtable must be QueryInterface, AddRef, and Release.
- The purpose and implementation of these methods will be discussed later.
-
-
- GUIDS
- ------------------------------------------------------------------------------
- A GUID is a Globally Unique ID. A GUID is a 16-byte number, that is unique
- to an interface. COM uses GUID's to identify different interfaces from one
- another. Using this method prevents name clashing as well as version
- clashing. To get a GUID, you use a generator utility that is included with
- most win32 development packages.
-
- A GUID is represented by the following structure:
-
- GUID STRUCT
- Data1 dd ?
- Data2 dw ?
- Data3 dw ?
- Data4 db 8 dup(?)
- GUID ENDS
-
- A GUID is then defined in the data section:
- MyGUID GUID <3F2504E0h, 4f89h, 11D3h, <9Ah, 0C3h, 0h, 0h, 0E8h, 2Ch, 3h, 1h>>
-
- Once a GUID is assigned to an interface and published, no furthur changes to
- the interface definition are allowed. Note, that this does mean that the
- interface implementation may not change, only the definition. For changes
- to the interface definition, a new GUID must be assigned.
-
-
- COM Objects
- ------------------------------------------------------------------------------
- A COM object is simply an implementation of an interface. Implementation
- details are not covered by the COM standard, so we are free to implement our
- objects as we choose, so long as they satisfy all the requirements of the
- interface definition.
-
- A typical object will contain pointers to the various interfaces it supports,
- a reference count, and any other data that the object needs. Here is a sample
- object definition, implemented as a structure:
-
- Object struct
- interface IInterface <?> ; pointer to an IInterface
- nRefCount dd ? ; reference count
- nValue dd ? ; private object data
- Object ends
-
- We also have to define the vtable's we are going to be using. These tables
- must be static, and cannot change during run-time. Each member of the vtable
- is a pointer to a method. Following is a method for defining the vtable.
-
- @@IInterface segment dword
- vtblIInterface:
- dd offset IInterface@QueryInterface
- dd offset IInterface@AddRef
- dd offset IInterface@Release
- dd offset IInterface@GetValue
- dd offset IInterface@SetValue
- @@IInterface ends
-
-
- Reference Counting
- ------------------------------------------------------------------------------
- COM object manage their lifetimes through reference counting. Each object
- maintains a reference count that keeps track of how many instances of the
- interface pointer have been created. The object is required to keep a
- counter that supports 2^32 instances, meaning the reference count must be a
- DWORD.
-
- When the reference count drops to zero, the object is no longer in use, and
- it destroys itself. The 2 IUnknown methods AddRef and Release handle the
- reference counting for a COM object.
-
-
- QueryInterface
- ------------------------------------------------------------------------------
- The QueryInterface method is used by a COM object to determine if the object
- supports a given interface, and then if supported, to get the interface
- pointer. There are 3 rules to implementing the QueryInterface method:
-
- 1. Objects must have an identity - a call to QueryInterface must always
- return the same pointer value.
- 2. The set of interfaces of an object must never change - for example, if
- a call to QueryInterface with on IID succeeds once, it must succeed
- always. Likewise, if it fails once, it must fail always.
- 3. It must be possible to successfully query an interface of an object
- from any other interface.
-
- QueryInterface returns a pointer to a specified interface on an object to
- which a client currently holds an interface pointer. This function must call
- the AddRef method on the pointer it returns.
-
- Following are the QueryInterface parameters:
- pif : [in] a pointer to the calling interface
- riid : [in] pointer to the IID of the interface being queried
- ppv : [out] pointer to the pointer of the interface that is to be set.
- If the interface is not supported, the pointed to value is set to 0
-
- QueryInterface returns the following:
- S_OK if the interface is supported
- E_NOINTERFACE if not supported
-
- Here is a simple assembly implementation of QueryInterface:
-
- IInterface@QueryInterface proc uses ebx pif:DWORD, riid:DWORD, ppv:DWORD
- ; The following compares the requested IID with the available ones.
- ; In this case, because IInterface inherits from IUnknown, the IInterface
- ; interface is prefixed with the IUnknown methods, and these 2 interfaces
- ; share the same interface pointer.
- invoke IsEqualGUID, [riid], addr IID_IInterface
- or eax,eax
- jnz @1
- invoke IsEqualGUID, [riid], addr IID_IUnknown
- or eax,eax
- jnz @1
- jmp @NoInterface
-
- @1:
- ; GETOBJECTPOINTER is a macro that will put the object pointer into eax,
- ; when given the name of the object, the name of the interface, and the
- ; interface pointer.
- GETOBJECTPOINTER Object, interface, pif
-
- ; now get the pointer to the requested interface
- lea eax, (Object ptr [eax]).interface
-
- ; set *ppv with this interface pointer
- mov ebx, [ppv]
- mov dword ptr [ebx], eax
-
- ; increment the reference count by calling AddRef
- GETOBJECTPOINTER Object, interface, pif
- mov eax, (Object ptr [eax]).interface
- invoke (IInterfaceVtbl ptr [eax]).AddRef, pif
-
- ; return S_OK
- mov eax, S_OK
- jmp return
-
- @NoInterface:
- ; interface not supported, so set *ppv to zero
- mov eax, [ppv]
- mov dword ptr [eax], 0
-
- ; return E_NOINTERFACE
- mov eax, E_NOINTERFACE
-
- return:
- ret
- IInterface@QueryInterface endp
-
-
- AddRef
- ------------------------------------------------------------------------------
- The AddRef method is used to increment the reference count for an interface
- of an object. It should be called for every new copy of an interface pointer
- to an object.
-
- AddRef takes no parameters, other than the interface pointer required for all
- methods. AddRef should return the new reference count. However, this value
- is to be used by callers only for testing purposes, as it may be unstable in
- certain situations.
-
- Following is a simple implementation of the AddRef method:
-
- IInterface@AddRef proc pif:DWORD
- GETOBJECTPOINTER Object, interface, pif
- ; increment the reference count
- inc [(Object ptr [eax]).nRefCount]
- ; now return the count
- mov eax, [(Object ptr [eax]).nRefCount]
- ret
- IInterface@AddRef endp
-
-
- Release
- ------------------------------------------------------------------------------
- Release decrements the reference count for the calling interface on a object.
- If the reference count on the object is decrememnted to 0, then the object is
- freed from memory. This function should be called when you no longer need to
- use an interface pointer
-
- Like AddRef, Release takes only one parameter - the interface pointer. It
- also returns the current value of the reference count, which, similarly, is to
- be used for testing purposess only
-
- Here is a simple implementation of Release:
-
- IInterface@Release proc pif:DWORD
- GETOBJECTPOINTER Object, interface, pif
-
- ; decrement the reference count
- dec [(Object ptr [eax]).nRefCount]
-
- ; check to see if the reference count is zero. If it is, then destroy
- ; the object.
- mov eax, [(Object ptr [eax]).nRefCount]
- or eax, eax
- jnz @1
-
- ; free the object: here we have assumed the object was allocated with
- ; LocalAlloc and with LMEM_FIXED option
- GETOBJECTPOINTER Object, interface, pif
- invoke LocalFree, eax
- @1:
- ret
- IInterface@Release endp
-
-
- Creating a COM object
- ------------------------------------------------------------------------------
- Creating an object consists basically of allocating the memory for the
- object, and then initializing its data members. Typically, the vtable
- pointer is initialized and the reference count is zeroed. QueryInterface
- could then be called to get the interface pointer.
-
- Other methods exist for creating objects, such as using CoCreateInstance, and
- using class factories. These methods will not be discussed, and may be a
- topic for a future article.
-
-
- COM implementatiion sample application
- ------------------------------------------------------------------------------
- Here follows a sample implementation and usage of a COM object. It shows how
- to create the object, call its methods, then free it. It would probably be
- very educational to assemble this and run it through a debugger. This and
- other examples can be found at http://asm.tsx.org.
-
-
- .386
- .model flat,stdcall
-
- include windows.inc
- include kernel32.inc
- include user32.inc
-
- includelib kernel32.lib
- includelib user32.lib
- includelib uuid.lib
-
- ;-----------------------------------------------------------------------------
- ; Macro to simply interface declarations
- ; Borrowed from Ewald, http://here.is/diamond/
- STDMETHOD MACRO name, argl :VARARG
- LOCAL @tmp_a
- LOCAL @tmp_b
- @tmp_a TYPEDEF PROTO argl
- @tmp_b TYPEDEF PTR @tmp_a
- name @tmp_b ?
- ENDM
-
- ; Macro that takes an interface pointer and returns the implementation
- ; pointer in eax
- GETOBJECTPOINTER MACRO Object, Interface, pif
- mov eax, pif
- IF (Object.Interface)
- sub eax, Object.Interface
- ENDIF
- ENDM
-
- ;-----------------------------------------------------------------------------
- IInterface@QueryInterface proto :DWORD, :DWORD, :DWORD
- IInterface@AddRef proto :DWORD
- IInterface@Release proto :DWORD
- IInterface@Get proto :DWORD
- IInterface@Set proto :DWORD, :DWORD
-
- CreateObject proto :DWORD
- IsEqualGUID proto :DWORD, :DWORD
-
- externdef IID_IUnknown:GUID
-
- ;-----------------------------------------------------------------------------
- ; declare the interface prototype
- IInterface struct
- lpVtbl dd ?
- IInterface ends
-
- IInterfaceVtbl struct
- ; IUnknown methods
- STDMETHOD QueryInterface, pif:DWORD, riid:DWORD, ppv:DWORD
- STDMETHOD AddRef, pif:DWORD
- STDMETHOD Release, pif:DWORD
- ; IInterface methods
- STDMETHOD GetValue, pif:DWORD
- STDMETHOD SetValue, pif:DWORD, val:DWORD
- IInterfaceVtbl ends
-
-
- ; declare the object structure
- Object struct
- ; interface object
- interface IInterface <?>
-
- ; object data
- nRefCount dd ?
- nValue dd ?
- Object ends
-
- ;-----------------------------------------------------------------------------
- .data
- ; define the vtable
- @@IInterface segment dword
- vtblIInterface:
- dd offset IInterface@QueryInterface
- dd offset IInterface@AddRef
- dd offset IInterface@Release
- dd offset IInterface@GetValue
- dd offset IInterface@SetValue
- @@IInterface ends
-
- ; define the interface's IID
- ; {CF2504E0-4F89-11d3-9AC3-0000E82C0301}
- IID_IInterface GUID <0cf2504e0h, 04f89h, 011d3h, <09ah, 0c3h, 00h, 00h,
- 0e8h, 02ch, 03h, 01h>>
-
- ;-----------------------------------------------------------------------------
- .code
- start:
- StartProc proc
- LOCAL pif:DWORD ; interface pointer
-
- ; create the object
- invoke CreateObject, addr [pif]
- or eax,eax
- js exit
-
- ; call the SetValue method
- mov eax, [pif]
- mov eax, [eax]
- invoke (IInterfaceVtbl ptr [eax]).SetValue, [pif], 12345h
-
- ; call the GetValue method
- mov eax, [pif]
- mov eax, [eax]
- invoke (IInterfaceVtbl ptr [eax]).GetValue, [pif]
-
- ; release the object
- mov eax, [pif]
- mov eax, [eax]
- invoke (IInterfaceVtbl ptr [eax]).Release, [pif]
-
- exit:
- ret
- StartProc endp
-
- ;-----------------------------------------------------------------------------
- IInterface@QueryInterface proc uses ebx pif:DWORD, riid:DWORD, ppv:DWORD
- invoke IsEqualGUID, [riid], addr IID_IInterface
- test eax,eax
- jnz @F
- invoke IsEqualGUID, [riid], addr IID_IUnknown
- test eax,eax
- jnz @F
- jmp @Error
-
- @@:
- GETOBJECTPOINTER Object, interface, pif
- lea eax, (Object ptr [eax]).interface
-
- ; set *ppv
- mov ebx, [ppv]
- mov dword ptr [ebx], eax
-
- ; increment the reference count
- GETOBJECTPOINTER Object, interface, pif
- mov eax, (Object ptr [eax]).interface
- invoke (IInterfaceVtbl ptr [eax]).AddRef, [pif]
-
- ; return S_OK
- mov eax, S_OK
- jmp return
-
- @Error:
- ; error, interface not supported
- mov eax, [ppv]
- mov dword ptr [eax], 0
- mov eax, E_NOINTERFACE
-
- return:
- ret
- IInterface@QueryInterface endp
-
-
- IInterface@AddRef proc pif:DWORD
- GETOBJECTPOINTER Object, interface, pif
- inc [(Object ptr [eax]).nRefCount]
- mov eax, [(Object ptr [eax]).nRefCount]
- ret
- IInterface@AddRef endp
-
-
- IInterface@Release proc pif:DWORD
- GETOBJECTPOINTER Object, interface, pif
- dec [(Object ptr [eax]).nRefCount]
- mov eax, [(Object ptr [eax]).nRefCount]
- or eax, eax
- jnz @1
- ; free object
- mov eax, [pif]
- mov eax, [eax]
- invoke LocalFree, eax
- @1:
- ret
- IInterface@Release endp
-
-
- IInterface@GetValue proc pif:DWORD
- GETOBJECTPOINTER Object, interface, pif
- mov eax, (Object ptr [eax]).nValue
- ret
- IInterface@GetValue endp
-
-
- IInterface@SetValue proc uses ebx pif:DWORD, val:DWORD
- GETOBJECTPOINTER Object, interface, pif
- mov ebx, eax
- mov eax, [val]
- mov (Object ptr [ebx]).nValue, eax
- ret
- IInterface@SetValue endp
-
- ;-----------------------------------------------------------------------------
- CreateObject proc uses ebx ecx pobj:DWORD
- ; set *ppv to 0
- mov eax, pobj
- mov dword ptr [eax], 0
-
- ; allocate object
- invoke LocalAlloc, LMEM_FIXED, sizeof Object
- or eax, eax
- jnz @1
- ; alloc failed, so return
- mov eax, E_OUTOFMEMORY
- jmp return
- @1:
-
- mov ebx, eax
- mov (Object ptr [ebx]).interface.lpVtbl, offset vtblIInterface
- mov (Object ptr [ebx]).nRefCount, 0
- mov (Object ptr [ebx]).nValue, 0
-
- ; Query the interface
- lea ecx, (Object ptr [ebx]).interface
- mov eax, (Object ptr [ebx]).interface.lpVtbl
- invoke (IInterfaceVtbl ptr [eax]).QueryInterface,
- ecx,
- addr IID_IInterface,
- [pobj]
- cmp eax, S_OK
- je return
-
- ; error in QueryInterface, so free memory
- push eax
- invoke LocalFree, ebx
- pop eax
-
- return:
- ret
- CreateObject endp
-
- ;-----------------------------------------------------------------------------
- IsEqualGUID proc rguid1:DWORD, rguid2:DWORD
- cld
- mov esi, [rguid1]
- mov edi, [rguid2]
- mov ecx, sizeof GUID / 4
- repe cmpsd
- xor eax, eax
- or ecx, ecx
- setz al
- ret
- IsEqualGUID endp
-
- end start
-
-
- Conclusion
- ------------------------------------------------------------------------------
- We have (hopefully) seen how to implement a COM object. We can see that it
- is a bit messy to do, and adds quite some overhead to our programs. However,
- it can also add great flexibility and power to our programs.
-
- Remember that COM defines only interfaces, and implementation is left to the
- programmer. This article presents only one possible implementation. This is
- not the only method, nor is it the best one. The reader should feel free to
- experiment with other methods.
-
- Copyright (C) 1999 Bill Tyler (billasm@usa.net)
-
-
-
- ::/ \::::::.
- :/___\:::::::.
- /| \::::::::.
- :| _/\:::::::::.
- :| _|\ \::::::::::.
- :::\_____\:::::::::::..........................................FEATURE.ARTICLE
- How to use DirectDraw in ASM
- by X-Calibre [Diamond]
-
-
- Well, there has been quite a large demand for this essay, so I finally started
- writing it. This essay will show you how to use C++ objects and COM interface
- in Win32ASM, using DirectDraw as an example.
-
- Well, in this part of the Win32 API, you will soon find out how important it
- is to know C and C++ when you want to use an API written in these languages.
- Judging from the demand for this essay, I think it will be necessary to
- explain a bit of how objects work in C++. I will not go too deep, but only
- show the things you need to know in Win32ASM.
-
- What are objects really?
-
- Actually a structure is an object of which all fields are public. We will look
- at it the other way around. So the public fields in an object make up a
- structure. The other fields in an object are private and are not reachable
- from the outside. So they are not interesting to us.
-
- A special thing about objects is that they can contain pointers to functions.
- Normally, when using C or ASM, this would be possible, but a bit error-prone.
- It can be seen as 'dirty' programming. That's why you probably haven't seen it
- before.
-
- When using C++ with a compiler, there will be no errors, as long as the
- compiler does its job. So here you can use this technique with no chance of
- errors, and it gives you some nice new programming options.
-
- C++ goes even further with this 'structure of functions' idea. With
- inheritance, you can also overwrite functions of the base class in the
- inherited class. You can also create 'virtual' functions, which are defined in
- the base class, but the actual code is only in inherited classes.
-
- This is of course interesting for DirectX, where you want to have standard
- functions, but with different code, depending on the hardware on which it is
- running. So in DirectX, all functions are defined as virtual, and the base
- class is inherited by hardware-specific drivers which supply hardware-specific
- code. And the beauty of this is, that it's all transparent to the programmer.
- The function pointers can change at runtime because of this system, so the C++
- designers had to think of a way to keep the pointers to the functions
- available to the program at all time.
-
- What this all boils down to is that there is a table with pointers to the
- functions. It's called the Virtual Function Table. I will call this the
- vtable from now on.
-
- So we need to get this table, in order to call functions from our object.
- Lucky for you, Z-Nith has already made a C program to 'capture' the table,
- and converted the resulting header file to an include file for use with MASM.
- So I'll just explain how you should use this table, and you can get going
- soon.
-
- Well, actually it's quite simple. The DirectX objects are defined like this:
-
- IDirectDraw STRUC
- lpVtbl DWORD ?
- IDirectDraw ENDS
-
- IDirectDrawPalette STRUC
- lpVtbl DWORD ?
- IDirectDrawPalette ENDS
-
- IDirectDrawClipper STRUC
- lpVtbl DWORD ?
- IDirectDrawClipper ENDS
-
- IDirectDrawSurface STRUC
- lpVtbl DWORD ?
- IDirectDrawSurface ENDS
-
- So these structs are actually just a pointer to the vtables, and don't contain
- any other values. Well, this makes it all very easy for us then.
- I'll give you a small example:
-
- Say we have an IDirectDraw object called lpDD. And we want to call the
- RestoreDisplayMode function.
- Then we need to do 2 things:
-
- 1. Get the vtable.
- 2. Get the address of the function, using the vtable.
-
- The first part is simple. All the struct contains, is the pointer to the
- vtable. So we can just do this:
-
- mov eax, [lpDD]
- mov eax, [eax]
-
- Simple, isn't it? And the next part isn't really much harder. The vtable is
- put into a structure called IDirectDrawVtbl in DDRAW.INC. We now have the
- address of the structure in eax. All we have to do now, is get the correct
- member of that structure, to get the address of the function we want to call.
- You would have guessed by now, that this will do the trick:
-
- call [IDirectDrawVtbl.RestoreDisplayMode][eax]
-
- That is not a bad guess...
- But there's one more thing, which is very important: this function needs to be
- invoked on the IDirectDraw object. We may only see the vtable in the structure,
- but there are also private members inside the object. So there's more than
- meets the eye here. What it comes down to is that the call needs the object
- as an argument. And this will be done by stack as always. So we just need to
- push lpDD before we call. The complete call will look like this:
-
- push [lpDD]
- call [IDirectDrawVtbl.RestoreDisplayMode][eax]
-
- Simple, was it not? And calls with arguments are not much harder.
- Let's set the displaymode to 320x200 in 32 bits next.
- This call requires 3 arguments:
-
- SetDisplayMode( width, height, bpp );
-
- Well, the extra arguments work just like normal API calls: just push them onto
- the stack in backward order.
- So it will look like this:
-
- push 32
- push 200
- push 320
- mov eax, [lpDD]
- push eax
- mov eax, [eax]
- call [IDirectDrawVtbl.SetDisplayMode][eax]
-
- And that's all there is to it.
-
- To make life easier, we have included some MASM macros in DDRAW.INC, for use
- with the IDirectDraw and IDirectDrawSurface objects:
-
- DDINVOKE MACRO func, this, arglist :VARARG
- mov eax, [this]
- mov eax, [eax]
-
- IFB <arglist>
- INVOKE [IDirectDrawVtbl. func][eax], this
- ELSE
- INVOKE [IDirectDrawVtbl. func][eax], this, arglist
- ENDIF
- ENDM
-
- DDSINVOKE MACRO func, this, arglist :VARARG
- mov eax, [this]
- mov eax, [eax]
-
- IFB <arglist>
- INVOKE [IDirectDrawSurfaceVtbl. func][eax], this
- ELSE
- INVOKE [IDirectDrawSurfaceVtbl. func][eax], this, arglist
- ENDIF
- ENDM
-
- With these macros, our 2 example calls will look as simple as this:
-
- DDINVOKE RestoreDisplayMode, lpDD
-
- DDINVOKE SetDisplayMode, lpDD, 320, 200, 32
-
-
- Well, that's basically all there is to know about using objects, COM and
- DirectX in Win32ASM. Have fun with it!
-
- And remember:
-
- C and C++ knowledge is power!
-
-
-
- ::/ \::::::.
- :/___\:::::::.
- /| \::::::::.
- :| _/\:::::::::.
- :| _|\ \::::::::::.
- :::\_____\:::::::::::...........................................FEATURE.ARTICLE
- Writing Boot Sectors To Disk
- by Jan Verhoeven
-
-
- Introduction.
- -------------
- In my previous article I showed how to make a private non-bootable
- bootsector for 1.44 Mb floppy disks. Unfortunately, there was no way yet to
- write that non-bootsector to a floppy disk....
-
- Enter this code. It is the accompanying bootsector writer for floppy disks.
- It assumes that your A: drive is the 1.44 Mb floppy disk drive and I dare
- say that this will be true in the majority of cases.
-
-
- The assembler used
- ------------------
- As usual, I have written this code in A86 format. Until now, not many
- aspects of the A86 extensions have been used, but, believe me, in future
- articles this will be done.
-
- A86 is particularly useful for people that make syntax errors. It will
- insert the errormessages into the sourcefile so that you can easily find
- them back. In the next assembler run the error messages are removed again.
-
- To fully use this aspect of A86 programming, I made a small batchfile that
- will let me choose between several options while writing the code. Below
- you can see the file. After an error, I choose to go back into the editor.
- When there are no errors, I might decided to do a trial run. Or to quit to
- DOS.
-
- This is all done by means of the WACHT command which waits for a keypress.
- It returns (in errorlevel) the indexed position in the command tail table
- of th key which was pressed.
-
-
- Rapid assembly prototyping.
- ---------------------------
- For easy processing and running sourcefiles I use a small batchfile, which
- looks like:
-
- ----------- Run.Bat --------------------------------------- Start ---------
- @echo off
- if "%1" == "" goto leave
-
- :start
- ed %1.a86
- a86 %1.a86 %2 %3 %4 %5 %6
-
- :menu
- Echo *
- Echo Options:
- Echo *Escape = stop
- Echo * L = LIST
- echo * ;-() = back to the editor
- echo * space = test-run of %1.com
- echo *Period = debugger-run with %1.com/sym
-
- wacht .\=-[]';-()/":?><{}|+_LCE
-
- if errorlevel 27 goto start
- if errorlevel 26 goto screen
- if errorlevel 25 goto list
- if errorlevel 4 goto start
- if errorlevel 3 goto debugger
- if errorlevel 2 goto execute
- if errorlevel 1 exit
- goto menu
-
- :execute
- %1
- if errorlevel 9 echo Errorlevel = 9+
- if errorlevel 8 echo Errorlevel = 8
- if errorlevel 7 echo Errorlevel = 7
- if errorlevel 6 echo Errorlevel = 6
- if errorlevel 5 echo Errorlevel = 5
- if errorlevel 4 echo Errorlevel = 4
- if errorlevel 3 echo Errorlevel = 3
- if errorlevel 2 echo Errorlevel = 2
- if errorlevel 1 echo Errorlevel = 1
- goto menu
-
- :debugger
- vgamode 3
- d86 %1
- goto menu
-
- :list
- list
- goto menu
-
- :screen
- vgamode 3
- goto menu
-
- :leave
- echo No file specified
- ----------- Run.Bat ---------------------------------------- End ----------
-
- This BAT file relies heavily on my computer system. For one, I use DR-DOS 6
- which means that I can use the EXIT word to get out of a Batchfile.
-
- Also, I switch videomodes back to Mode 3 with "Vgamode 3" and you will have
- to use another command for that, like "Mode co80" or using the utillity
- that came with your videocard.
-
- The program "List" is Vernon Buerg's
-
-